package com.xucia.jsponic.rpc;

import java.lang.reflect.Method;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;

import com.xucia.jsponic.data.Persistable;
import com.xucia.jsponic.data.GlobalData;
import com.xucia.jsponic.remote.ClientConnection;

public class RPCall extends RPCMessage{
	protected String functionName;
	protected Persistable target;
	protected Object[] parameters;
	private static int nextId = 1;

	public RPCall(Persistable target, String functionName, Object[] parameters) {
		super();
		id = "call" + nextId++;
		this.target = target;
		this.functionName = functionName;
		this.parameters = parameters;
	}
	public RPCall(Persistable target, String functionName, Object[] parameters, String id) {
		super();
		this.id = id;
		this.target = target;
		this.functionName = functionName;
		this.parameters = parameters;
	}

	public String getFunctionName() {
		return functionName;
	}

	public Object[] getParameters() {
		return parameters;
	}

	public Persistable getTarget() {
		return target;
	}
	public static final Object SUSPENDED = new Object();

	/**
	 * Executes the Remote Procedure call on the server. It will generate a response when finished
	 *
	 */
	public void executeLocally(){
		try {
			if (target == null)
    			throw new RuntimeException("Can not execute a procedure without an object defined");
			Object jsMethod = target.get(functionName);
			if (jsMethod instanceof Function) {
				Object retValue = ((Function)jsMethod).call(Context.enter(), GlobalData.getGlobalScope(), target, parameters);
				if (retValue == SUSPENDED)
					return;
				else
					ClientConnection.getCurrentObjectResponse().sendMessage(new RPCResponse(id,retValue,null));
				return;
			}
			else {
				Class[] paramTypes;
				paramTypes = new Class[parameters.length];
		    	for (int j =0; j < parameters.length; j++) {
		    		paramTypes[j] = parameters[j].getClass(); 
		    	}
		    	Method targetMethod = target.getClass().getMethod(functionName, paramTypes);
				RemotelyExecutable executable = targetMethod.getAnnotation(RemotelyExecutable.class);
				if (!(executable.executable().newInstance()).isExecutable(this))
					throw new RuntimeException("You are not permitted to execute this method");
				ClientConnection.getCurrentObjectResponse().sendMessage(new RPCResponse(id,targetMethod.invoke(target, parameters),null));

			}
		}
		catch (Throwable e) {
			ClientConnection.getCurrentObjectResponse().sendMessage(new RPCResponse(id,null,e.getMessage()));
		}
	}
}
